package level.editor.control.select;

import utility.ImageUtility;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import javax.imageio.ImageIO;

import level.editor.control.layer.Layer;
import level.editor.control.layer.LayerBuffer;
import level.editor.control.main.Main;
import level.model.Addable;
import level.model.Selection;
import level.model.Tile;

public class SelectBuffer extends LayerBuffer
{

	public SelectBuffer(Integer zOrder, Main main)
	{
		super(zOrder, main);
		setSize(main.getSize());
		main.revalidate();
		images = new TreeMap<String, Image>();
		this.selection = new Selection();
		image = null;
		topLeft = new Point();
		outline = new ArrayList<Line2D>();
		initGhost = null;
		endGhost = null;
	}

	public Selection getSelection()
	{
		return selection;
	}

	public void add(Selection selection)
	{
		this.selection.add(selection);
		makeImage();
	}

	public void finish()
	{
		// Put the selection back into main
		Map<Integer, Layer> layers = main.getLayers();
		for (int i : layers.keySet())
		{
			if (selection.get(i) != null && !selection.get(i).isEmpty())
			{
				for (Addable addable : selection.get(i))
				{
					layers.get(i).addObject(addable);
				}

				layers.get(i).repaint();
			}
		}

		// initGhost = null;
		// endGhost = null;
		selection.clear();

		makeImage();

		// main.remove(this);
	}

	public void delete()
	{
		if (!selection.isEmpty())
		{
			selection.clear();
			makeImage();
			main.minimapUpdate();
			main.repaint();
		}

		initGhost = null;
		endGhost = null;
	}

	public void setInitGhost(Point init)
	{
		initGhost = init;
	}

	public void setEndGhost(Point end)
	{
		endGhost = end;
	}

	public Rectangle getRectangle()
	{
		if (initGhost != null && endGhost != null)
		{
			Rectangle rect = new Rectangle(Math.min(initGhost.x, endGhost.x),
					Math.min(initGhost.y, endGhost.y), Math.abs(endGhost.x
							- initGhost.x), Math.abs(endGhost.y - initGhost.y));
//			System.out.println(rect.x + " " + rect.y + " ; " + rect.width + " "
//					+ rect.height);
			return rect;
		}
		
		else
			return null;
	}

	@Override
	public void paintComponent(Graphics gOld)
	{
		setSize(getParent().getSize());
		Graphics2D g = (Graphics2D) gOld;

		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);

		if (image != null)
		{
			g.drawImage(image, -topLeft.x + main.getOrigin().x, -topLeft.y
					+ main.getOrigin().y, null);
		}

		if (initGhost != null && endGhost != null
				&& !(initGhost.x == endGhost.x && initGhost.y == endGhost.y))
		{
			g.setStroke(BASIC_STROKE);
			Rectangle rect = new Rectangle(Math.min(initGhost.x, endGhost.x)
					+ main.getOrigin().x, Math.min(initGhost.y, endGhost.y)
					+ main.getOrigin().y, Math.abs(endGhost.x - initGhost.x),
					Math.abs(endGhost.y - initGhost.y));
			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
					0.5f));
			g.setColor(GHOST_INSIDE_COLOR);
			g.fill(rect);
			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
					1f));
			g.setColor(GHOST_OUTLINE_COLOR);
			g.draw(rect);
		}
	}

	private void makeImage()
	{
		image = new BufferedImage(this.getWidth(), this.getHeight(),
				BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = image.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);

		outline = new ArrayList<Line2D>();

		for (Integer i : selection.keySet())
		{
			for (Addable addable : selection.get(i))
			{
				if (addable instanceof Tile)
				{
					addTileToImage((Tile) addable, g);
				}
			}
		}
		drawOutline(g);
		g.dispose();
	}

	private void addTileToImage(Tile addable, Graphics2D g)
	{
		topLeft.x = (int) main.getOrigin().x;
		topLeft.y = (int) main.getOrigin().y;

		g.setStroke(DASHED_STROKE);
		g.setColor(OUTLINE_COLOR);
		Tile tile = (Tile) addable;
		if (!images.containsKey(tile.getIconPath()))
		{
			try
			{
				images.put(tile.getIconPath(), ImageUtility
						.makeColorTransparent(
								ImageIO.read(new File(tile.getIconPath())),
								new Color(255, 0, 255)));
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}

		g.drawImage(images.get(tile.getIconPath()),
				(int) (tile.getX() + topLeft.x),
				(int) (tile.getY() + topLeft.y),
				(int) (tile.getX() + topLeft.x + tile.getW()),
				(int) (tile.getY() + topLeft.y + tile.getH()),
				(int) tile.getSx(), (int) tile.getSy(), (int) tile.getSx()
						+ tile.getSw(), (int) tile.getSy() + tile.getSh(), null);

		addToOutline(new Rectangle((int) (tile.getX() + topLeft.x),
				(int) (tile.getY() + topLeft.y), (int) tile.getW(),
				(int) tile.getH()));

		// g.drawRect((int) (tile.getX() + topLeft.x),
		// (int) (tile.getY() + topLeft.y), (int) tile.getW(),
		// (int) tile.getH());
	}

	private void addToOutline(Rectangle rect)
	{
		Line2D line1 = new Line2D.Float(rect.x, rect.y, rect.x, rect.y
				+ rect.height);
		Line2D line2 = new Line2D.Float(rect.x, rect.y, rect.x + rect.width,
				rect.y);
		Line2D line3 = new Line2D.Float(rect.x, rect.y + rect.height, rect.x
				+ rect.width, rect.y + rect.height);
		Line2D line4 = new Line2D.Float(rect.x + rect.width, rect.y, rect.x
				+ rect.width, rect.y + rect.height);

		// System.out.println(line1.getX1() + "  " + line1.getX2());
		// System.out.println(line2);
		// System.out.println(line3);
		// System.out.println(line4);

		checkLine(line1);
		checkLine(line2);
		checkLine(line3);
		checkLine(line4);
	}

	private void checkLine(Line2D line)
	{
		for (Line2D l : outline)
		{
			// EWWWWWWWW
			if (((l.getX1() == line.getX1() || l.getX1() == line.getX1() - 1 || l
					.getX1() == line.getX1() + 1)
					&& (l.getY1() == line.getY1()
							|| l.getY1() == line.getY1() - 1 || l.getY1() == line
							.getY1() + 1)
					&& (l.getX2() == line.getX2()
							|| l.getX2() == line.getX2() - 1 || l.getX2() == line
							.getX2() + 1) && (l.getY2() == line.getY2()
					|| l.getY2() == line.getY2() - 1 || l.getY2() == line
					.getY2() + 1))
					|| ((l.getX1() == line.getX2()
							|| l.getX1() == line.getX2() - 1 || l.getX1() == line
							.getX2() + 1)
							&& (l.getY1() == line.getY2()
									|| l.getY1() == line.getY2() - 1 || l
									.getY1() == line.getY2() + 1)
							&& (l.getX2() == line.getX1()
									|| l.getX2() == line.getX1() - 1 || l
									.getX2() == line.getX1() + 1) && (l.getY2() == line
							.getY1() || l.getY2() == line.getY1() - 1 || l
							.getY2() == line.getY1() + 1)))
			{
				outline.remove(l);
				return;
			}
		}
		outline.add(line);
	}

	private void drawOutline(Graphics2D g)
	{
		for (Line2D l : outline)
		{
			g.draw(l);
		}
	}

	private Point initGhost;
	private Point endGhost;
	private Selection selection;
	private TreeMap<String, Image> images;
	private BufferedImage image;
	private Point topLeft;
	private ArrayList<Line2D> outline;
	private final static float dash1[] =
	{ 8f };
	private final static Stroke BASIC_STROKE = new BasicStroke(3f);
	private final static Stroke DASHED_STROKE = new BasicStroke(3f,
			BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 5f, dash1, 0f);
	private final static Color OUTLINE_COLOR = Color.YELLOW;
	private final static Color GHOST_OUTLINE_COLOR = new Color(0, 162, 232);
	private final static Color GHOST_INSIDE_COLOR = new Color(153, 217, 234);
}
